﻿Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Text
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Globalization
Imports System.Collections.ObjectModel
Imports System.Data
Imports System.Drawing
Imports System.Web.UI.HtmlControls

Namespace BingMaps
    <DefaultProperty("Text"), ToolboxData("<{0}:BingMap runat=server></{0}:BingMap>")> _
    Public Class BingMap
        Inherits WebControl

        ' define variables
        Private BingMapObject As Object
        Shared Property UseGoogle As Boolean = False
        Private Const ContainerId As String = "mapDiv"
        Private Const DefaultContainerTag As String = "div"
        Private Const BingMapVariableName As String = "map"
        Private Const BingMapControlHref As String = "http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v={0}"
        Private Const GoogleMapControlHref As String = "http://maps.google.com/maps/api/js?sensor=false"
        Private js As StringBuilder
        Private Layers As List(Of Layer)
        Private InfoboxContents As List(Of InfoBoxContent)
        Private ChangeColor As String = ""
        Shared Property InfoBoxes As List(Of InfoBox)
        Property CustomizeOverlays As Boolean = False
        Property DisableKeyboardInput As Boolean = False
        Property ShowTraffic As Boolean = False
        Property UseBingTheme As Boolean = False
        Property Animate As Boolean = True
        Property LabelOverlayOff As Boolean = False
        Property DisableBirdseye As Boolean = False
        Property ShowBreadcrumb As Boolean = False
        Property DisableZooming As Boolean = False
        Property DisablePanning As Boolean = False
        Property ShowMapTypeSelector As Boolean = True
        Property EnableSearchLogo As Boolean = False
        Property EnableClickableLogo As Boolean = False
        Property Padding As Integer = 1
        Property AddPinAtClickPosition As Boolean = False
        Property DefaultPushPinOffset As String = "0, 0"
        Shared Property UseNewLine As Boolean = False
        Property ShowDashboard As Boolean = True
        Property DisableMouseInput As Boolean = False
        Property DisableTouchInput As Boolean = True
        Property DisableUserInput As Boolean = False
        Property FixedMapPosition As Boolean = False
        Property ShowCopyright As Boolean = False
        Property UseInertia As Boolean = False
        Property Lat As Double
        Property Lng As Double
        Property ShowScaleBar As Boolean = True
        Property ApiVersion As String = "7.0"
        Property BingMapKey As String
        Property Width As String = "100%"
        Property Height As String = "100%"
        Property Zoom As Integer = 5
        Property MapType As MapTypeEnum = MapTypeEnum.Aerial
        Property ShowCurrentPosition As Boolean = False
        Property Scripts As List(Of Script)
        Property Print As Boolean = False
        Private _boundsLatLngField As String
        Private _centerLatLngField As String
        Private _spanLatLngField As String
        Private curLngField As String = "curLngField"
        Private curLatField As String = "curLatField"
        Private bSetPosition As Boolean = False
        Property Result As String = ""
        Property DisableDefaultUI As Boolean = False
        Property EnableMapTypeControl As Boolean = True
        Property MapTypeControlStyle As MapTypeControlStyleEnum = MapTypeControlStyleEnum.HORIZONTAL_BAR
        Property MapTypeControlPosition As PositionEnum = PositionEnum.TOP_RIGHT
        Property VisibleMapTypeIds As List(Of String)
        Property EnableNavigationControl As Boolean = True
        Property NavigationControlPosition As PositionEnum = PositionEnum.TOP_LEFT
        Property NavigationControlStyle As NavigationControlStypeEnum = NavigationControlStypeEnum.ZOOM_PAN
        Property EnableScaleControl As Boolean = True
        Property EnableKeyboardShortcuts As Boolean = True
        Property DisableDoubleClickZoom As Boolean = False
        Property Draggable As Boolean = True
        Property EnableScrollwheel As Boolean = True
        Property EnableStreetViewControl As Boolean = True
        Property NoClear As Boolean = False
        Property BackgroundColor As System.Drawing.Color = Color.Gray
        Property DraggableCursor As DraggableCursorEnum = DraggableCursorEnum.Xpointer
        Property DraggableCursorURL As String = ""
        Property DraggingCursor As DraggableCursorEnum = DraggableCursorEnum.Xmove
        Property DraggingCursorURL As String = ""
        Property MapOptions As List(Of MapOption)

        ' events

        Public Sub AddMapType(sName As MapTypeEnum)
            ' add maptypes to show on the control
            Select Case sName
                Case MapTypeEnum.Auto, MapTypeEnum.Birdseye
                    Throw New Exception("This MapType is not allowed for this map!")
            End Select

            Dim tName As String
            Select Case sName
                Case MapTypeEnum.Road
                    tName = "ROADMAP"
                Case Else
                    tName = sName.ToString.ToUpper
            End Select
            VisibleMapTypeIds.Add(tName)
        End Sub

        Public Class InfoboxContent
            ' update infobox content
            Property ID As String
            Property Content As String

            Public Sub New()
                MyBase.New()
            End Sub

            Public Sub New(sID As String, sContent As String)
                ID = sID
                Content = sContent
            End Sub

            Public Sub Render(js As StringBuilder)
                js.AppendFormat(CultureInfo.InvariantCulture, "{0}_Infobox.setContent('{1}');", ID, Content)
            End Sub

        End Class


        Public Sub SetInfoBoxContent(PinID As String, sContent As String)
            ' set infobox contents
            Dim iContent As New InfoboxContent(PinID, sContent)
            InfoboxContents.Add(iContent)
        End Sub


        Public Sub SetOption(oOptionType As OptionTypesEnum, oOptionValue As String)
            ' set options of google maps
            Dim nOption As New MapOption(oOptionType, oOptionValue)
            MapOptions.Add(nOption)
        End Sub

        Public Enum OptionTypesEnum
            ' options to set
            Zoom
            MapType
            Center
        End Enum

        Public Class MapOption
            ' stores values for options to set on the map
            Property OptionType As OptionTypesEnum
            Property OptionValue As String = ""

            Public Sub New()
                MyBase.New()
            End Sub

            Public Sub New(oOptionType As OptionTypesEnum, oOptionValue As Object)
                Me.OptionType = oOptionType
                Me.OptionValue = oOptionValue
            End Sub

            Public Sub Render(js As StringBuilder, ByVal RenderTo As String)
                ' render the mapoption
                Select Case OptionType
                    Case OptionTypesEnum.Zoom
                        js.AppendFormat(CultureInfo.InvariantCulture, "{0}.setOptions({{zoom: {1}}});", RenderTo, OptionValue)
                    Case OptionTypesEnum.MapType
                        If UseGoogle = True Then
                            js.AppendFormat(CultureInfo.InvariantCulture, "{0}.setOptions({{mapTypeId: google.maps.MapTypeId.{1}}});", RenderTo, OptionValue)
                        Else
                            js.AppendFormat(CultureInfo.InvariantCulture, "{0}.setOptions({{mapTypeId: Microsoft.Maps.MapTypeId.{1}}});", RenderTo, OptionValue)
                        End If
                    Case OptionTypesEnum.Center
                        js.AppendFormat(CultureInfo.InvariantCulture, "{0}.setCenter({1});", RenderTo, OptionValue)
                End Select
            End Sub
        End Class

        'Public Event MapClick As EventHandler()

        'Public Overridable Sub OnMapClick(ByVal args As EventArgs)
        '    RaiseEvent MapClick(Me, args)
        'End Sub

        ' define the e arguements, we want to get the point to the click event
        ' of the map
        'Public Class MapClickEventArgs
        ' Inherits System.EventArgs
        ' Public Property Point() As Point
        'Public Property Cancel() As Boolean

        'Public Sub New(oPoint As Point)
        '    Point = oPoint
        'End Sub
        'End Class

        ' close events

        Public Class Script
            ' class to hold scripts in the map application
            Property FunctionName As String = ""
            Property Arguements As String = ""
            Property Code As String = ""

            Public Sub New()
                'create a new class of bounds
                MyBase.New()
            End Sub

            Public Sub New(sFuncName As String)
                ' add a new script
                FunctionName = sFuncName
            End Sub

            Public Sub New(sFuncName As String, sArguements As String)
                ' add a new script
                FunctionName = sFuncName
                Arguements = sArguements
            End Sub


            Public Sub New(sFuncName As String, sArguements As String, sCode As String)
                ' add a new script
                FunctionName = sFuncName
                Arguements = sArguements
                Code = sCode
            End Sub

            Public Sub Render(js As StringBuilder, RenderTo As String)
                ' render the script
                js.AppendFormat(CultureInfo.InvariantCulture, "function {0}({1})", FunctionName, Arguements)
                If UseNewLine = True Then js.AppendLine()
                js.Append("{")
                If UseNewLine = True Then js.AppendLine()
                Code = Code.Replace("<map>", RenderTo)
                js.Append(Code)
                js.Append("}")
            End Sub

        End Class

        Public Sub AddScript(sFunctionName As String, sArguements As String, ByVal ParamArray CodePieces() As Object)
            ' add a script to the map, if there is any reference to the map, use "<map>" on the code pieces
            Dim nScript As New Script(sFunctionName, sArguements)
            Dim sCode As New StringBuilder
            For Each cPiece As String In CodePieces
                sCode.Append(cPiece)
                If UseNewLine = True Then js.AppendLine()
            Next
            nScript.Code = sCode.ToString
            _Scripts.Add(nScript)
        End Sub

        Public Enum NavigationControlStypeEnum
            ' map control styles for google
            SMALL
            ANDROID
            ZOOM_PAN
        End Enum

        Public Enum PositionEnum
            ' control positions for google
            BOTTOM
            BOTTOM_LEFT
            BOTTOM_RIGHT
            LEFT
            RIGHT
            TOP
            TOP_LEFT
            TOP_RIGHT
        End Enum

        Public Enum DraggableCursorEnum
            ' define the cursor to use when dragging something
            Xcrosshair
            Xdefault
            Xhelp
            Xmove
            Xpointer
            Xtext
            Xwait
        End Enum

        Public Class Bounds
            ' define the view area
            Public Property Points As List(Of Point)
            Public Sub New()
                'create a new class of bounds
                MyBase.New()
                Points = New List(Of Point)
            End Sub

            Public Sub AddPoint(Lat As Double, Lng As Double)
                ' add a point for the bounds
                Dim nPoint As New Point
                nPoint.Lat = Lat
                nPoint.Lng = Lng
                Points.Add(nPoint)
            End Sub

            Public Sub AddPoint(pT As Point)
                ' add a point to the bounds class
                Points.Add(pT)
            End Sub

            Public Sub Render(js As StringBuilder, RenderTo As String, MapName As String)
                ' render the bounds
                Dim sbBoundaries As New StringBuilder

                If UseGoogle = True Then
                    For Each pt As Point In Points
                        If sbBoundaries.Length > 0 Then sbBoundaries.Append(",")
                        sbBoundaries.AppendFormat(CultureInfo.InvariantCulture, "new google.maps.LatLng({0}, {1})", pt.Lat.ToString(CultureInfo.InvariantCulture),
                                                  pt.Lng.ToString(CultureInfo.InvariantCulture))
                        If UseNewLine = True Then js.AppendLine()
                    Next
                    If Points.Count - 1 >= 0 Then
                        js.AppendFormat(CultureInfo.InvariantCulture,
                        "var {0}_bounds = new google.maps.LatLngBounds({1});", RenderTo, sbBoundaries.ToString)
                        js.AppendFormat(CultureInfo.InvariantCulture, "{0}.fitBounds({1}_bounds);", MapName, RenderTo)
                        If UseNewLine = True Then js.AppendLine()
                    End If

                Else
                    For Each pT As Point In Points
                        If sbBoundaries.Length > 0 Then sbBoundaries.Append(",")
                        sbBoundaries.AppendFormat(CultureInfo.InvariantCulture, "new Microsoft.Maps.Location({0}, {1})", pT.Lat.ToString(CultureInfo.InvariantCulture),
                                                  pT.Lng.ToString(CultureInfo.InvariantCulture))
                        If UseNewLine = True Then js.AppendLine()
                    Next
                    If Points.Count - 1 >= 0 Then
                        js.AppendFormat(CultureInfo.InvariantCulture,
                        "var {0}_bounds = Microsoft.Maps.LocationRect.fromLocations({1});", RenderTo, sbBoundaries.ToString)
                        js.AppendFormat(CultureInfo.InvariantCulture, "{0}.setView({{bounds: {1}_bounds}});", MapName, RenderTo)
                        If UseNewLine = True Then js.AppendLine()
                    End If
                End If
            End Sub
        End Class

        Private Sub GetCurrentPosition(js As StringBuilder, RenderTo As String)
            ' get the current users position
            js.AppendFormat(CultureInfo.InvariantCulture, "var {0}_GeolocationProvider = new Microsoft.Maps.GeoLocationProvider({0});", RenderTo)
            If UseNewLine = True Then js.AppendLine()
            js.AppendFormat(CultureInfo.InvariantCulture, "{0}_GeolocationProvider.getCurrentPosition();", RenderTo)
            If UseNewLine = True Then js.AppendLine()
        End Sub

        Private Shared Function GetARGB(cc As System.Drawing.Color, Optional IsTransparent As Boolean = False) As String
            ' to make the color transparent, set A to be 127
            ' istransparent is used for polygons
            If IsTransparent = True Then
                Return "127," & cc.R.ToString & "," & cc.G.ToString & "," & cc.G.ToString
            Else
                Return cc.A.ToString & "," & cc.R.ToString & "," & cc.G.ToString & "," & cc.G.ToString
            End If
        End Function

        Private Shared Function GetHEX(cc As System.Drawing.Color) As String
            ' return the hex from system drawing
            Return "#" + cc.R.ToString("X2") + cc.G.ToString("X2") + cc.B.ToString("X2")
        End Function

        Public Function AddInfoBox(sID As String, Lat As Double, Lng As Double, sTitle As String, sDescription As String, bVisible As Boolean) As InfoBox
            ' add an inforbox to the map
            Dim nIB As New InfoBox
            nIB.ID = sID & "_Infobox"
            nIB.Title = sTitle
            nIB.Description = sDescription
            nIB.Visible = bVisible
            nIB.Lat = Lat
            nIB.Lng = Lng
            nIB.Pushpin = sID
            InfoBoxes.Add(nIB)
            Return nIB
        End Function

        Public Function AddInfoBox(oPin As PushPin) As InfoBox
            ' add an inforbox to the map
            Dim nIB As New InfoBox
            nIB.ID = oPin.ID & "_Infobox"
            nIB.Title = oPin.Title
            nIB.Description = oPin.ToolTip
            nIB.Visible = oPin.Visible
            nIB.Lat = oPin.Lat
            nIB.Lng = oPin.Lng
            nIB.Pushpin = oPin.ID
            nIB.ShowOnClickOnly = oPin.ShowInfoBoxOnClick
            InfoBoxes.Add(nIB)
            Return nIB
        End Function

        Public Sub AddInfoBox(nBox As InfoBox)
            If Len(nBox.ID) = 0 Then nBox.ID = nBox.Pushpin & "_Infobox"
            InfoBoxes.Add(nBox)
        End Sub

        Public Enum MapTypeControlStyleEnum
            DROPDOWN_MENU
            HORIZONTAL_BAR
        End Enum

        Public Enum FileType
            ' used when importing shapes from files
            IsKML
            IsAsriShape
            IsNone
            IsDbf
            IsDataTable
            IsCSV
            IsGeoRSS
        End Enum

        Public Enum LayerType
            ' what the layer will contain
            Polyline
            Polygon
            Pushpin
        End Enum

        Public Class Layer
            ' the layer class
            Property ID As String
            Property OfType As LayerType
            Property Bubble As Boolean = True
            Property zIndex As Boolean = 0
            Property SourceFile As String = ""
            Property SourceFileType As FileType = FileType.IsNone
            Private _fromFile As Boolean = False
            Property Polylines As List(Of Polyline)
            Property Polygons As List(Of Polygon)
            Property Pushpins As List(Of PushPin)
            Property Description As String = ""
            Property DataLatField As String = ""
            Property DataLngField As String = ""
            Property DataPushpinIdField As String = ""
            Property DataIconField As String = ""
            Property DataDescField As String = ""
            Property Visible As Boolean = True
            Property SetToBounds As Boolean = False

            Public Sub New()
                'create a new classs
                MyBase.New()
                Pushpins = New List(Of PushPin)
                Polylines = New List(Of Polyline)
                Polygons = New List(Of Polygon)
            End Sub

            Public Sub New(ByVal sID As String, ByVal sDescription As String, lOfType As LayerType, bBubble As Boolean, bVisible As Boolean, zIndex As Integer)
                MyBase.New()
                Pushpins = New List(Of PushPin)
                Polylines = New List(Of Polyline)
                Polygons = New List(Of Polygon)
                Me.Description = sDescription
                Me.ID = sID
                Me.OfType = lOfType
                Me.Bubble = bBubble
                Me.Visible = bVisible
                Me.zIndex = zIndex
            End Sub

            Public Sub AddKML(sSourceFile As String)
                SourceFile = sSourceFile
                SourceFileType = FileType.IsKML
                _fromFile = True
            End Sub

            Public Sub AddAsriShape(sSourceFile As String)
                SourceFile = sSourceFile
                SourceFileType = FileType.IsAsriShape
                _fromFile = True
            End Sub

            Public Sub AddDbf(sSourceFile As String)
                SourceFile = sSourceFile
                SourceFileType = FileType.IsDbf
                _fromFile = True
            End Sub

            'Public Sub AddDataTable(dtTable As datatable)
            '    SourceFile = dtTable.name
            '    SourceFileType = FileType.IsDataTable
            '    _fromFile = False
            'End Sub

            
            
            Public Sub AddPushpin(nPushPin As PushPin)
                ' add a pushpin to a later
                If Me.OfType = LayerType.Pushpin Then
                    Pushpins.Add(nPushPin)
                Else
                    Throw New Exception("The layer type is not of type pushpin.")
                End If
            End Sub

            Public Sub AddPolygon(nPolygon As Polygon)
                ' add a polygon to the layer
                If Me.OfType = LayerType.Polygon Then
                    Polygons.Add(nPolygon)
                Else
                    Throw New Exception("The layer type is not of type polygon.")
                End If
            End Sub

            Public Function AddPolygon(ByVal sID As String, ByVal sDescription As String, cFillColor As System.Drawing.Color, cStrokeColor As System.Drawing.Color, iStrokeThickness As Integer, sStrokeDashArray As String, bZoomToPoints As Boolean) As Polygon
                ' add a new polygon
                Dim nPolygon As New Polygon
                With nPolygon
                    .FillColor = cFillColor
                    .StrokeColor = cStrokeColor
                    .ID = sID
                    .Description = sDescription
                    .StrokeThickness = iStrokeThickness
                    .StrokeDashArray = sStrokeDashArray
                    .ZoomToPoints = bZoomToPoints
                End With
                Polygons.Add(nPolygon)
                Return nPolygon
            End Function

            Public Function AddPolyline(ByVal sID As String, ByVal sDescription As String, cStrokeColor As System.Drawing.Color, iStrokeThickness As Integer, sStrokeDashArray As String, bZoomToPoints As Boolean) As Polyline
                ' add a new polyline
                Dim nPolyline As New Polyline
                With nPolyline
                    .StrokeColor = cStrokeColor
                    .ID = sID
                    .Description = sDescription
                    .StrokeThickness = iStrokeThickness
                    .StrokeDashArray = sStrokeDashArray
                    .ZoomToPoints = bZoomToPoints
                End With
                Polylines.Add(nPolyline)
                Return nPolyline
            End Function


            Public Sub AddPolyline(nPolyline As Polyline)
                ' add a polyline to the layer
                If Me.OfType = LayerType.Polyline Then
                    Polylines.Add(nPolyline)
                Else
                    Throw New Exception("The layer type is not of type polyline.")
                End If
            End Sub

            Public Function AddPushPin(sID As String, Lat As Double, Lng As Double, Text As String) As PushPin
                ' add a pushpin to the layer and return it
                If Me.OfType = LayerType.Pushpin Then
                    Dim nPP As New PushPin(sID, Lat, Lng, Text)
                    AddPushPin(nPP)
                    Return nPP
                Else
                    Throw New Exception("The layer type is not of type pushpin.")
                End If
            End Function

            Public Function AddPushPin(sID As String, Lat As Double, Lng As Double) As PushPin
                ' add a pushpin to the layer
                If Me.OfType = LayerType.Pushpin Then
                    Dim nPP As New PushPin(sID, Lat, Lng, "")
                    AddPushPin(nPP)
                    Return nPP
                Else
                    Throw New Exception("The layer type is not of type pushpin.")
                End If
            End Function

            Public Function AddPushPin(sID As String, Lat As Double, Lng As Double, Text As String, IconURL As String) As PushPin
                ' add a pushpin to the layer
                If Me.OfType = LayerType.Pushpin Then
                    Dim nPP As New PushPin(sID, Lat, Lng, Text, IconURL)
                    AddPushPin(nPP)
                    Return nPP
                Else
                    Throw New Exception("The layer type is not of type polyline.")
                End If
            End Function

            Public Function AddPushpingWithInfoBox(sID As String, Lat As Double, Lng As Double, sToolTip As String, IconURL As String, sHTMLContent As String)
                ' add a pushpin to the layer
                If Me.OfType = LayerType.Pushpin Then
                    Dim nPP As New PushPin(sID, Lat, Lng, "", IconURL)
                    nPP.Title = sToolTip
                    nPP.ShowToolTip = True
                    nPP.ToolTip = sToolTip
                    AddPushpin(nPP)
                    Dim nIB As InfoBox = nPP.AddInfoBox(sHTMLContent, Not nPP.ShowInfoBoxOnClick, nPP.ShowInfoBoxOnClick)
                    nIB.Description = sHTMLContent
                    nIB.Lat = Lat
                    nIB.Lng = Lng
                    Return nPP
                Else
                    Throw New Exception("The layer type is not of type polyline.")
                End If
            End Function

            Public Function AddPushpingWithToolTip(sID As String, Lat As Double, Lng As Double, sToolTip As String, IconURL As String)
                ' add a pushpin to the layer
                If Me.OfType = LayerType.Pushpin Then
                    Dim nPP As New PushPin(sID, Lat, Lng, "", IconURL)
                    nPP.Title = sToolTip
                    nPP.ShowToolTip = True
                    nPP.ToolTip = sToolTip
                    AddPushpin(nPP)
                    Return nPP
                Else
                    Throw New Exception("The layer type is not of type polyline.")
                End If
            End Function

            Public Function AddPushPin(sID As String, Lat As Double, Lng As Double, Description As String, IconURL As String, Optional InfoBoxVisible As Boolean = False, Optional ShowOnClickOnly As Boolean = False) As PushPin
                ' add a pushpin to the layer
                If Me.OfType = LayerType.Pushpin Then
                    Dim nPP As New PushPin(sID, Lat, Lng, "", IconURL)
                    AddPushPin(nPP)
                    Dim nIB As InfoBox = nPP.AddInfoBox("", Description, InfoBoxVisible, ShowOnClickOnly)
                    Return nPP
                Else
                    Throw New Exception("The layer type is not of type polyline.")
                End If
            End Function

        End Class

        Public Sub RemovePolyline(ByVal sID As String)
            ' remove a polyline from the collection
            Dim lCnt As Integer = 0
            Dim lTot As Integer = Layers.Count - 1
            Dim pCnt As Integer = 0
            For lCnt = 0 To lTot
                Dim pTot As Integer = Layers(lCnt).Polylines.Count - 1
                For pCnt = 0 To pTot
                    Dim curPolyline As String = Layers(lCnt).Polylines(pCnt).ID.ToLower
                    If sID.ToLower = curPolyline Then
                        Layers(lCnt).Polylines.RemoveAt(pCnt)
                        Exit Sub
                    End If
                Next
            Next
        End Sub

        Public Sub RemovePolylineByDescription(ByVal sDescription As String)
            ' remove a polyline from the collection
            Dim lCnt As Integer = 0
            Dim lTot As Integer = Layers.Count - 1
            Dim pCnt As Integer = 0
            For lCnt = 0 To lTot
                Dim pTot As Integer = Layers(lCnt).Polylines.Count - 1
                For pCnt = 0 To pTot
                    Dim curPolyline As String = Layers(lCnt).Polylines(pCnt).Description.ToLower
                    If sDescription.ToLower = curPolyline Then
                        Layers(lCnt).Polylines.RemoveAt(pCnt)
                        Exit Sub
                    End If
                Next
            Next
        End Sub


        Public Sub RemovePushpin(ByVal sID As String)
            ' remove a pushpin from the collection
            Dim lCnt As Integer = 0
            Dim lTot As Integer = Layers.Count - 1
            Dim pCnt As Integer = 0
            For lCnt = 0 To lTot
                Dim pTot As Integer = Layers(lCnt).Pushpins.Count - 1
                For pCnt = 0 To pTot
                    Dim curPushpin As String = Layers(lCnt).Pushpins(pCnt).ID.ToLower
                    If sID.ToLower = curPushpin Then
                        Layers(lCnt).Pushpins.RemoveAt(pCnt)
                        Exit Sub
                    End If
                Next
            Next
        End Sub

        Public Sub RemovePolygon(ByVal sID As String)
            ' remove a polygon from the collection
            Dim lCnt As Integer = 0
            Dim lTot As Integer = Layers.Count - 1
            Dim pCnt As Integer = 0
            For lCnt = 0 To lTot
                Dim pTot As Integer = Layers(lCnt).Polygons.Count - 1
                For pCnt = 0 To pTot
                    Dim curPolygon As String = Layers(lCnt).Polygons(pCnt).ID.ToLower
                    If sID.ToLower = curPolygon Then
                        Layers(lCnt).Polygons.RemoveAt(pCnt)
                        Exit Sub
                    End If
                Next
            Next
        End Sub

        Public Sub RemovePolygonByDescription(ByVal sDescription As String)
            ' remove a polygon from the collection
            Dim lCnt As Integer = 0
            Dim lTot As Integer = Layers.Count - 1
            Dim pCnt As Integer = 0
            For lCnt = 0 To lTot
                Dim pTot As Integer = Layers(lCnt).Polygons.Count - 1
                For pCnt = 0 To pTot
                    Dim curPolygon As String = Layers(lCnt).Polygons(pCnt).Description.ToLower
                    If sDescription.ToLower = curPolygon Then
                        Layers(lCnt).Polygons.RemoveAt(pCnt)
                        Exit Sub
                    End If
                Next
            Next
        End Sub

        Public Sub RemoveLayer(ByVal sID As String)
            ' remove a later from the collection by id
            Dim lTot As Integer = Layers.Count - 1
            Dim lCnt As Integer
            For lCnt = 0 To lTot
                Dim cLayer As String = Layers(lCnt).ID.ToString
                If sID.ToLower = cLayer Then
                    Layers.RemoveAt(lCnt)
                    Exit For
                End If
            Next
        End Sub

        Public Sub RemoveLayerByDescription(ByVal sDescription As String)
            ' remove a later from the collection by description
            Dim lTot As Integer = Layers.Count - 1
            Dim lCnt As Integer
            For lCnt = 0 To lTot
                Dim cLayer As String = Layers(lCnt).Description.ToString
                If sDescription.ToLower = cLayer Then
                    Layers.RemoveAt(lCnt)
                    Exit For
                End If
            Next
        End Sub

        Public Sub AddLayer(nLayer As Layer)
            Layers.Add(nLayer)
        End Sub

        Public Function AddLayer(ByVal sID As String, ByVal sDescription As String, OfType As LayerType, bBubble As Boolean, zIndex As Integer, Optional bVisible As Boolean = True) As Layer
            ' add a layer to the map
            Dim nLayer As New Layer
            nLayer.ID = sID
            nLayer.Bubble = bBubble
            nLayer.zIndex = zIndex
            nLayer.Visible = bVisible
            nLayer.OfType = OfType
            nLayer.Description = sDescription
            Layers.Add(nLayer)
            Return nLayer
        End Function

        Private Shared Function RemDelim(ByVal DataObj As String, ByVal Delimiter As String) As String
            ' remove the last delimiter of a string
            Dim intDataSize As Integer
            Dim intDelimSize As Integer
            Dim strLast As String
            intDataSize = Len(DataObj)
            intDelimSize = Len(Delimiter)
            strLast = Right(DataObj, intDelimSize)
            Select Case strLast
                Case Delimiter
                    Return Left(DataObj, (intDataSize - intDelimSize))
                Case Else
                    Return DataObj
            End Select
        End Function

        Public Enum MapTypeEnum
            ' the types of maps to view
            Auto
            Road
            Aerial
            Birdseye
            Hybrid
            Satellite
            Terrain
            AerialHybrid
            Physical
            MapMakerNormal
            MapMakerHybrid
            MoonElevation
            MoonVisible
            MarsElevation
            MarsVisible
            MarsInfrared
            SkyVisible
            Satellite3D
        End Enum




        Public Class Polygon
            ' the polygon class
            Property Points As List(Of Point)
            Property FillColor As System.Drawing.Color
            Property StrokeColor As System.Drawing.Color
            Property ID As String
            Property StrokeDashArray As String = "2 2 2 2"
            Property StrokeThickness As Integer = 0
            Property Visible As Boolean = True
            Property InfoBox As InfoBox
            Property ZoomToPoints As Boolean = False
            Property Description As String = ""

            Private Sub SetBounds(js As StringBuilder, RenderTo As String, MapName As String)
                ' use the points in the polygon to set the vieweing area
                Dim nBounds As New Bounds
                For Each pT In Me.Points
                    nBounds.AddPoint(pT)
                Next
                nBounds.Render(js, RenderTo, MapName)
            End Sub

            Public Sub Render(js As StringBuilder, RenderTo As String, MapName As String)
                ' render the js to the map for the polygon
                Dim sbPolygons As New StringBuilder
                For Each pPoint In Points
                    If sbPolygons.Length > 0 Then sbPolygons.Append(",")
                    sbPolygons.AppendFormat(CultureInfo.InvariantCulture, "new Microsoft.Maps.Location({0}, {1})", _
                                            pPoint.Lat.ToString(CultureInfo.InvariantCulture),
                                            pPoint.Lng.ToString(CultureInfo.InvariantCulture))
                    If UseNewLine = True Then js.AppendLine()
                Next
                js.AppendFormat(CultureInfo.InvariantCulture, "var {0}_vertices = new Array({1});", ID, sbPolygons.ToString)
                If UseNewLine = True Then js.AppendLine()
                js.AppendFormat(CultureInfo.InvariantCulture, "var {0} = new Microsoft.Maps.Polygon({0}_vertices, {{{1}}});", ID, GetOptions)
                If UseNewLine = True Then js.AppendLine()
                js.AppendFormat(CultureInfo.InvariantCulture, "{0}.push({1});", RenderTo, ID)
                If UseNewLine = True Then js.AppendLine()

                ' code to hide and show
                js.AppendFormat(CultureInfo.InvariantCulture, "function {0}showPolygon()", ID)
                If UseNewLine = True Then js.AppendLine()
                js.Append("{")
                If UseNewLine = True Then js.AppendLine()
                js.AppendFormat(CultureInfo.InvariantCulture, "{0}.setOptions({{visible:true}});", ID)
                If UseNewLine = True Then js.AppendLine()
                js.Append("}")
                If UseNewLine = True Then js.AppendLine()

                js.AppendFormat(CultureInfo.InvariantCulture, "function {0}hidePolygon()", ID)
                If UseNewLine = True Then js.AppendLine()
                js.Append("{")
                If UseNewLine = True Then js.AppendLine()
                js.AppendFormat(CultureInfo.InvariantCulture, "{0}.setOptions({{visible:false}});", ID)
                If UseNewLine = True Then js.AppendLine()
                js.Append("}")
                If UseNewLine = True Then js.AppendLine()
                If ZoomToPoints = True Then SetBounds(js, RenderTo, MapName)
            End Sub

            Private Function GetOptions() As String
                ' build the polygon options
                Dim ppOptions As New StringBuilder
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "fillColor: new Microsoft.Maps.Color({0}),", GetARGB(FillColor, True))
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "strokeColor: new Microsoft.Maps.Color({0}),", GetARGB(StrokeColor))
                If Len(StrokeDashArray) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "strokeDashArray: '{0}',", StrokeDashArray)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "visible: {0},", Visible.ToString.ToLower)
                If Len(StrokeThickness) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "strokeThickness: {0},", StrokeThickness)
                Return RemDelim(ppOptions.ToString, ",")
            End Function

            Public Sub New()
                'create a new classs
                MyBase.New()
                Points = New List(Of Point)
            End Sub

            Public Sub New(ByVal sDescription As String)
                Description = sDescription
            End Sub

            Public Sub AddPoint(Lat As Double, Lng As Double)
                ' add a point to the polygon
                Dim nPoint As New Point
                nPoint.Lat = Lat
                nPoint.Lng = Lng
                Points.Add(nPoint)
            End Sub
        End Class

        Public Class Polyline
            ' the polyline class
            Property Points As List(Of Point)
            Property StrokeColor As System.Drawing.Color
            Property ID As String
            Property StrokeDashArray As String = "2 2 2 2"
            Property StrokeThickness As Integer = 5
            Property Visible As Boolean = True
            Property InfoBox As InfoBox
            Property ZoomToPoints As Boolean = False
            Property Description As String = ""
            Property StrokeOpacity As Double = 0.7
            Property StrokeWeight As Integer = 5
            Property FillColor As System.Drawing.Color
            Property FillOpacity As Double = 0.5

            Public Sub New(ByVal sDescription As String)
                Description = sDescription
            End Sub

            Public Sub SetBounds(js As StringBuilder, RenderTo As String, MapName As String)
                ' use the points in the polygon to set the vieweing area
                Dim nBounds As New Bounds
                For Each pT In Me.Points
                    nBounds.AddPoint(pT)
                Next
                nBounds.Render(js, RenderTo, MapName)
            End Sub

            Public Sub Render(js As StringBuilder, RenderTo As String, MapName As String)
                ' render the polyline to the js of the map
                Dim sbPolygons As New StringBuilder
                For Each pPoint In Points
                    If sbPolygons.Length > 0 Then sbPolygons.Append(",")
                    sbPolygons.AppendFormat(CultureInfo.InvariantCulture, "new Microsoft.Maps.Location({0}, {1})", _
                                            pPoint.Lat.ToString(CultureInfo.InvariantCulture),
                                            pPoint.Lng.ToString(CultureInfo.InvariantCulture))
                    If UseNewLine = True Then js.AppendLine()
                Next
                js.AppendFormat(CultureInfo.InvariantCulture, "var {0}_vertices = new Array({1});", ID, sbPolygons.ToString)
                If UseNewLine = True Then js.AppendLine()
                js.AppendFormat(CultureInfo.InvariantCulture, "var {0} = new Microsoft.Maps.Polyline({0}_vertices, {{{1}}});", ID, GetOptions)
                If UseNewLine = True Then js.AppendLine()
                js.AppendFormat(CultureInfo.InvariantCulture, "{0}.push({1});", RenderTo, ID)
                If UseNewLine = True Then js.AppendLine()
                ' code to hide and show
                js.AppendFormat(CultureInfo.InvariantCulture, "function {0}showPolyline()", ID)
                If UseNewLine = True Then js.AppendLine()
                js.Append("{")
                If UseNewLine = True Then js.AppendLine()
                js.AppendFormat(CultureInfo.InvariantCulture, "{0}.setOptions({{visible:true}});", ID)
                If UseNewLine = True Then js.AppendLine()
                js.Append("}")
                If UseNewLine = True Then js.AppendLine()

                js.AppendFormat(CultureInfo.InvariantCulture, "function {0}hidePolyline()", ID)
                If UseNewLine = True Then js.AppendLine()
                js.Append("{")
                If UseNewLine = True Then js.AppendLine()
                js.AppendFormat(CultureInfo.InvariantCulture, "{0}.setOptions({{visible:false}});", ID)
                If UseNewLine = True Then js.AppendLine()
                js.Append("}")
                If UseNewLine = True Then js.AppendLine()
                If ZoomToPoints = True Then SetBounds(js, RenderTo, MapName)
            End Sub

            Private Function GetOptions() As String
                ' build the polyline options
                Dim ppOptions As New StringBuilder
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "strokeColor: new Microsoft.Maps.Color({0}),", GetARGB(StrokeColor))
                If Len(StrokeDashArray) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "strokeDashArray: '{0}',", StrokeDashArray)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "visible: {0},", Visible.ToString.ToLower)
                If Len(StrokeThickness) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "strokeThickness: {0},", StrokeThickness)
                Return RemDelim(ppOptions.ToString, ",")
            End Function

            Public Sub New()
                'create a new classs
                MyBase.New()
                Points = New List(Of Point)
            End Sub

            Public Sub AddPoint(Lat As Double, Lng As Double)
                ' add a point to the polyline
                Dim nPoint As New Point
                nPoint.Lat = Lat
                nPoint.Lng = Lng
                Points.Add(nPoint)
            End Sub
        End Class


        Public Class InfoBox
            ' the infor box class
            Property ID As String
            Property Lat As Double
            Property Lng As Double
            Property Title As String = ""
            Property Description As String = ""
            Property Visible As Boolean = False
            Property Offset As String = "0, 10"
            Property Height As String = "70"
            Property Width As String = "250"
            Property ShowPointer As Boolean = True
            Property ShowCloseButton As Boolean = True
            Property zIndex As Integer = 0
            Property Pushpin As String = ""
            Property HTMLContent As String = ""
            Property ShowTitle As Boolean = False
            Property ShowOnClickOnly As Boolean = False
            Property ImageURL As String = ""
            Property LinkText As String = ""
            Property LinkURL As String = ""

            Public Sub New()
                'create a new classs
                MyBase.New()
            End Sub

            Public Sub BuildContent()
                Dim sContent As New StringBuilder
                sContent.AppendFormat(CultureInfo.InvariantCulture, "<div id=""{0}_Content"">", ID)
                If Len(ImageURL) > 0 Then sContent.AppendFormat(CultureInfo.InvariantCulture, "<img src=""{0}"" alt="">", ImageURL)
                If Len(Title) > 0 Then sContent.AppendFormat(CultureInfo.InvariantCulture, "<h2>{0}</h2>", Title)
                If Len(Description) > 0 Then sContent.AppendFormat(CultureInfo.InvariantCulture, "<p>{0}</p>", Description)
                If Len(LinkURL) > 0 And Len(LinkText) > 0 Then sContent.AppendFormat(CultureInfo.InvariantCulture, "<p><a href=""{0}"">{1}</a></p>", LinkURL, LinkText)
                sContent.Append("</div>")
                HTMLContent = sContent.ToString
            End Sub
            
            Public Sub Render(js As StringBuilder, RenderTo As String)
                ' render the inforbox to the js of the map
                If UseGoogle = True Then
                    js.AppendFormat(CultureInfo.InvariantCulture, "var {0} = new google.maps.InfoWindow({{{1}}});", ID, GetOptions)
                    If UseNewLine = True Then js.AppendLine()

                    If ShowOnClickOnly = True Then
                        js.AppendFormat(CultureInfo.InvariantCulture, "google.maps.event.addListener({0}, 'click', function() {{{2}.open({1}, {0});}});", _
                           Pushpin, RenderTo, ID)
                        If UseNewLine = True Then js.AppendLine()
                    Else
                        js.AppendFormat(CultureInfo.InvariantCulture, "google.maps.event.trigger({0}, 'click');", ID)
                        If UseNewLine = True Then js.AppendLine()
                    End If
                    'js.AppendFormat(CultureInfo.InvariantCulture, "google.maps.event.addListener({0}, " &
                    '                "'click', function() {});", Pushpin, RenderTo)
                Else
                    js.AppendFormat(CultureInfo.InvariantCulture, "var {0}_pnIboxLoc = new Microsoft.Maps.Location({1},{2});", _
                                    ID, Lat.ToString, Lng.ToString)
                    If UseNewLine = True Then js.AppendLine()
                    js.AppendFormat(CultureInfo.InvariantCulture, "var {0} = new Microsoft.Maps.Infobox({0}_pnIboxLoc, {{{1}}});", _
                                    ID, GetOptions)
                    If UseNewLine = True Then js.AppendLine()

                    If ShowOnClickOnly = True Then
                        js.AppendFormat(CultureInfo.InvariantCulture, "{0}_MouseClick = Microsoft.Maps.Events.addHandler({0}, 'click', {1}showInfobox);", Pushpin, ID)
                        If UseNewLine = True Then js.AppendLine()
                    Else
                        js.AppendFormat(CultureInfo.InvariantCulture, "{0}_MouseOver = Microsoft.Maps.Events.addHandler({0}, 'mouseover', {1}showInfobox);", Pushpin, ID)
                        If UseNewLine = True Then js.AppendLine()
                        js.AppendFormat(CultureInfo.InvariantCulture, "{0}_MouseOut = Microsoft.Maps.Events.addHandler({0}, 'mouseout', {1}hideInfobox);", Pushpin, ID)
                        If UseNewLine = True Then js.AppendLine()
                    End If

                    js.AppendFormat(CultureInfo.InvariantCulture, "{0}_InforBoxes.push({1});", RenderTo, ID)
                    If UseNewLine = True Then js.AppendLine()

                    js.AppendFormat(CultureInfo.InvariantCulture, "function {0}showInfobox()", ID)
                    If UseNewLine = True Then js.AppendLine()
                    js.Append("{")
                    If UseNewLine = True Then js.AppendLine()
                    js.AppendFormat(CultureInfo.InvariantCulture, "{0}.setOptions({{visible:true}});", ID)
                    If UseNewLine = True Then js.AppendLine()
                    js.Append("}")
                    If UseNewLine = True Then js.AppendLine()

                    js.AppendFormat(CultureInfo.InvariantCulture, "function {0}hideInfobox()", ID)
                    If UseNewLine = True Then js.AppendLine()
                    js.Append("{")
                    If UseNewLine = True Then js.AppendLine()
                    js.AppendFormat(CultureInfo.InvariantCulture, "{0}.setOptions({{visible:false}});", ID)
                    If UseNewLine = True Then js.AppendLine()
                    js.Append("}")
                    If UseNewLine = True Then js.AppendLine()
                End If
            End Sub

            Public Sub New(oPin As PushPin)
                ' add an inforbox to the map
                ID = oPin.ID & "_Infobox"
                Title = oPin.Title
                Description = oPin.ToolTip
                Visible = oPin.Visible
                Lat = oPin.Lat
                Lng = oPin.Lng
                Pushpin = oPin.ID
            End Sub

            Public Sub New(sTitle As String, sDescription As String, sPushpin As String, Optional zIndex As Integer = 0)
                ' create a new inforbox
                Title = sTitle
                Description = sDescription
                Pushpin = sPushpin
                Me.zIndex = zIndex
            End Sub

            Public Sub New(sDescription As String, sPushpin As String, Optional zIndex As Integer = 0)
                ' create a new inforbox
                Description = sDescription
                Pushpin = sPushpin
                Me.zIndex = zIndex
            End Sub

            Private Function GetOptions() As String
                ' build the inforbox options
                Dim ppOptions As New StringBuilder
                If UseGoogle = True Then
                    If Len(HTMLContent) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "content: '{0}',", HTMLContent)
                Else
                    If ShowTitle = True Then
                        If Len(Title) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "title: '{0}',", Title)
                    End If
                    If Len(Description) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "description: '{0}',", Description)
                    ppOptions.AppendFormat(CultureInfo.InvariantCulture, "visible: {0},", Visible.ToString.ToLower)
                    If Len(Offset) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "offset: new Microsoft.Maps.Point({0}),", Offset)
                    If Len(Height) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "height: {0},", Height)
                    If Len(Width) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "width: {0},", Width)
                    If ShowPointer = False Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "showPointer: {0},", ShowPointer.ToString.ToLower)
                    If ShowCloseButton = False Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "showCloseButton: {0},", ShowCloseButton.ToString.ToLower)
                    ppOptions.AppendFormat(CultureInfo.InvariantCulture, "zIndex: {0},", zIndex.ToString)
                    If Len(Pushpin) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "pushpin: {0},", Pushpin.ToString)
                    If Len(HTMLContent) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "htmlContent: ""{0}"",", HTMLContent)
                End If
                Return RemDelim(ppOptions.ToString, ",")
            End Function
        End Class


        Private Function GetOptions() As String
            ' build the map options
            Dim ppOptions As New StringBuilder

            If UseGoogle = True Then
                Dim mapTypeS As String
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "zoom: {0},", Zoom)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "center: new google.maps.LatLng({0},{1}),", Me.Lat, Me.Lng)
                Select Case MapType
                    Case MapTypeEnum.Auto, MapTypeEnum.Birdseye
                        Throw New Exception("The MapType that you have specified is invalid for this map!")
                    Case MapTypeEnum.Road
                        mapTypeS = "ROADMAP"
                    Case Else
                        mapTypeS = MapType.ToString.ToUpper
                End Select

                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "mapTypeId: google.maps.MapTypeId.{0},", mapTypeS)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "disableDefaultUI: {0},", DisableDefaultUI.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "mapTypeControl: {0},", EnableMapTypeControl.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "mapTypeControlOptions: {{style: google.maps.MapTypeControlStyle.{0}, " & _
                                       "position: google.maps.ControlPosition.{1}}},", MapTypeControlStyle.ToString, MapTypeControlPosition)

                ' build the maptypes to show
                Dim mapTypesToShow As New StringBuilder
                For Each mt As String In VisibleMapTypeIds
                    If mapTypesToShow.Length > 0 Then mapTypesToShow.Append(",")
                    mapTypesToShow.AppendFormat(CultureInfo.InvariantCulture, "google.maps.MapTypeId.{0}", mt)
                Next
                ' add maptypes to options
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "mapTypeIds: [{0}],", mapTypesToShow.ToString)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "navigationControl: {0},", EnableNavigationControl.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "navigationControlOptions: {{style: google.maps.NavigationControlStyle.{0}, " & _
                                       "position: google.maps.ControlPosition.{1}}},", NavigationControlStyle.ToString, NavigationControlPosition)

                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "scaleControl: {0},", EnableScaleControl.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "keyboardShortcuts: {0},", EnableKeyboardShortcuts.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "disableDoubleClickZoom: {0},", DisableDoubleClickZoom.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "draggable: {0},", Draggable.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "scrollwheel: {0},", EnableScrollwheel.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "streetViewControl: {0},", EnableStreetViewControl.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "noClear: {0},", NoClear.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "backgroundColor: '{0}',", GetHEX(BackgroundColor))
                If Len(DraggableCursorURL) > 0 Then
                    ppOptions.AppendFormat(CultureInfo.InvariantCulture, "draggableCursor: '{0}',", DraggableCursorURL)
                Else
                    ppOptions.AppendFormat(CultureInfo.InvariantCulture, "draggableCursor: '{0}',", DraggableCursor.ToString.Substring(1))
                End If
                If Len(DraggingCursorURL) > 0 Then
                    ppOptions.AppendFormat(CultureInfo.InvariantCulture, "draggingCursor: '{0}',", DraggingCursorURL)
                Else
                    ppOptions.AppendFormat(CultureInfo.InvariantCulture, "draggingCursor: '{0}',", DraggingCursor.ToString.Substring(1))
                End If


            Else
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "credentials: ""{0}"",", BingMapKey)

                Select Case MapType
                    Case MapTypeEnum.Auto, MapTypeEnum.Road, MapTypeEnum.Aerial, MapTypeEnum.Birdseye, MapTypeEnum.Hybrid
                    Case Else
                        Throw New Exception("The MapType that you have specified is invalid for this map!")
                End Select
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "mapTypeId: Microsoft.Maps.MapTypeId.{0},", MapType.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "zoom: {0},", Zoom.ToString)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "center: new Microsoft.Maps.Location({0}, {1}),", Lat.ToString, Lng.ToString)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "showScaleBar: {0},", ShowScaleBar.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "padding: {0},", Padding.ToString)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "enableClickableLogo: {0},", EnableClickableLogo.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "enableSearchLogo: {0},", EnableSearchLogo.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "showDashboard: {0},", ShowDashboard.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "showMapTypeSelector: {0},", ShowMapTypeSelector.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "disablePanning: {0},", DisablePanning.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "disableZooming: {0},", DisableZooming.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "showBreadcrumb: {0},", ShowBreadcrumb.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "disableBirdseye: {0},", DisableBirdseye.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "animate: {0},", Animate.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "disableKeyboardInput: {0},", DisableKeyboardInput.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "disableMouseInput: {0},", DisableMouseInput.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "disableTouchInput: {0},", DisableTouchInput.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "disableUserInput: {0},", DisableUserInput.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "fixedMapPosition: {0},", FixedMapPosition.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "showCopyright: {0},", ShowCopyright.ToString.ToLower)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "useInertia: {0},", UseInertia.ToString.ToLower)

                If LabelOverlayOff = True Then ppOptions.Append("labelOverlay: Microsoft.Maps.LabelOverlay.hidden,")

                'ppOptions.AppendFormat(CultureInfo.InvariantCulture, "customizeOverlays: {0},", CustomizeOverlays.ToString.ToLower)

                If UseBingTheme = True Then
                    ppOptions.AppendFormat(CultureInfo.InvariantCulture, "theme: new Microsoft.Maps.Themes.BingTheme(),")
                End If
            End If
            Return RemDelim(ppOptions.ToString, ",")
        End Function

        Public Class PushPin
            ' the pushpin class
            Property ID As String
            Property Lat As Double
            Property Lng As Double
            Property Text As String = ""
            Property Width As Integer = 25
            Property Height As Integer = 39
            Property IconURL As String = ""
            Property Draggable As Boolean = False
            Property Visible As Boolean = True
            Property TextOffset As String = "0,10"
            Property ShowToolTip As Boolean = False
            Property ShowText As Boolean = False
            Property ToolTip As String = ""
            Property MoveTo As Boolean = False
            Property ZoomLevel As Integer = 11
            Property Anchor As String = "10,10"
            Property TypeName As String = ""
            Property HoverIconURL As String = ""
            Property Title As String = ""
            Property ImageURL As String = ""
            Property LinkText As String = ""
            Property LinkURL As String = ""
            Property ShowInfoBoxOnClick As Boolean = True

            Public Sub New()
                'create a new classs
                MyBase.New()
            End Sub

            Private Function GetOptions(Optional ByVal RenderTo As String = "") As String
                ' build the pushpin options
                Dim ppOptions As New StringBuilder
                If UseGoogle = True Then
                    ' we are using google map
                    ppOptions.AppendFormat(CultureInfo.InvariantCulture, "position: new google.maps.LatLng({0},{1}),", Lat, Lng)
                    ppOptions.AppendFormat(CultureInfo.InvariantCulture, "map: {0},", RenderTo)
                    If ShowToolTip = True Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "title: '{0}',", Title)
                    If Len(IconURL) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "icon: '{0}',", IconURL)

                'If Len(TextOffset) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "textOffset: new Microsoft.Maps.Point({0}),", TextOffset)

                Else
                If ShowText = True Then
                    If Len(Text) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "text: '{0}',", Text)
                    If Len(TextOffset) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "textOffset: new Microsoft.Maps.Point({0}),", TextOffset)
                End If
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "visible: {0},", Visible.ToString.ToLower)
                If Len(TypeName) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "typeName: '{0}',", TypeName)
                ppOptions.AppendFormat(CultureInfo.InvariantCulture, "draggable: {0},", Draggable.ToString.ToLower)
                If Len(Anchor) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "anchor: new Microsoft.Maps.Point({0}),", Anchor)
                If Len(IconURL) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "icon: '{0}',", IconURL)
                If Len(HoverIconURL) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "hoverIcon: '{0}',", HoverIconURL)
                If Len(Width) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "width: {0},", Width)
                If Len(Height) > 0 Then ppOptions.AppendFormat(CultureInfo.InvariantCulture, "height: {0},", Height)
                End If
                Return RemDelim(ppOptions.ToString, ",")
            End Function

            Public Sub Render(js As StringBuilder, RenderTo As String, MapName As String)
                If UseGoogle = True Then
                    ' we are using a google map
                    If UseNewLine = True Then js.AppendLine()
                    js.AppendFormat(CultureInfo.InvariantCulture, "var {0} = new google.maps.Marker({{{1}}});", ID, GetOptions(MapName))
                    If UseNewLine = True Then js.AppendLine()
                Else
                    ' render the pushpin to the map js
                    If UseNewLine = True Then js.AppendLine()
                    js.AppendFormat(CultureInfo.InvariantCulture, "var {0}_ppLoc = new Microsoft.Maps.Location({1},{2});", _
                                    ID, Lat, Lng)
                    If UseNewLine = True Then js.AppendLine()
                    js.AppendFormat(CultureInfo.InvariantCulture, "var {0} = new Microsoft.Maps.Pushpin({0}_ppLoc, {{{1}}});", _
                                ID, GetOptions)
                    If UseNewLine = True Then js.AppendLine()
                    js.AppendFormat(CultureInfo.InvariantCulture, "{0}.push({1});", RenderTo, ID)
                    If UseNewLine = True Then js.AppendLine()

                    ' add tooltip
                    If ShowToolTip = True Then js.AppendFormat(CultureInfo.InvariantCulture, "$('.{0}').children('img').attr('title','{1}.');", ID, ToolTip)
                    If UseNewLine = True Then js.AppendLine()
                    If MoveTo = True Then
                        js.AppendFormat(CultureInfo.InvariantCulture, "{0}.setView({{center: {1}_ppLoc, zoom: {2}}});", MapName, ID, ZoomLevel)
                        If UseNewLine = True Then js.AppendLine()
                    End If

                    ' code to hide and show
                    js.AppendFormat(CultureInfo.InvariantCulture, "function {0}showPushpin()", ID)
                    If UseNewLine = True Then js.AppendLine()
                    js.Append("{")
                    If UseNewLine = True Then js.AppendLine()
                    js.AppendFormat(CultureInfo.InvariantCulture, "{0}.setOptions({{visible:true}});", ID)
                    If UseNewLine = True Then js.AppendLine()
                    js.Append("}")
                    If UseNewLine = True Then js.AppendLine()

                    js.AppendFormat(CultureInfo.InvariantCulture, "function {0}hidePushpin()", ID)
                    If UseNewLine = True Then js.AppendLine()
                    js.Append("{")
                    If UseNewLine = True Then js.AppendLine()
                    js.AppendFormat(CultureInfo.InvariantCulture, "{0}.setOptions({{visible:false}});", ID)
                    If UseNewLine = True Then js.AppendLine()
                    js.Append("}")
                    If UseNewLine = True Then js.AppendLine()
                End If
            End Sub

            Public Sub SetToolTip(nTooltip As String)
                ' set the tooltip of the pushpin on
                ShowToolTip = True
                ToolTip = nTooltip
            End Sub

            Public Sub SetText(nText As String)
                ' set the text of the pushpin
                ShowText = True
                Text = nText
            End Sub

            Public Sub New(sID As String, dLat As Double, dLng As Double)
                ' create a new pushpin
                ID = sID
                Lat = dLat
                Lng = dLng
            End Sub

            Public Sub New(sID As String, dLat As Double, dLng As Double, sText As String)
                ' create a new pushpin
                ID = sID
                Lat = dLat
                Lng = dLng
                Text = sText
                ShowText = True
            End Sub

            Public Sub New(sID As String, dLat As Double, dLng As Double, sText As String, sIconURL As String)
                ' add pushpin
                ID = sID
                Lat = dLat
                Lng = dLng
                Text = sText
                IconURL = sIconURL
                ShowText = True
            End Sub

            Public Sub New(sID As String, dLat As Double, dLng As Double, sText As String, sIconURL As String, sWidth As String, sHeight As String)
                ' add pushpin
                ID = sID
                Lat = dLat
                Lng = dLng
                Text = sText
                IconURL = sIconURL
                Width = sWidth
                Height = sHeight
                ShowText = True
            End Sub

            Public Sub New(sID As String, dLat As Double, dLng As Double, sText As String, sIconURL As String, sWidth As String, sHeight As String, bDraggable As Boolean)
                ' add pushpin
                ID = sID
                Lat = dLat
                Lng = dLng
                Text = sText
                IconURL = sIconURL
                Width = sWidth
                Height = sHeight
                Draggable = bDraggable
                ShowText = True
            End Sub

            Public Function AddInfoBox(sTitle As String, sDescription As String, bVisible As Boolean, ShowOnClickOnly As Boolean) As InfoBox
                ' add infobox to a pushpin
                Dim nIB As New InfoBox
                nIB.ID = ID & "_Infobox"
                nIB.Title = sTitle
                nIB.Description = sDescription
                nIB.Visible = bVisible
                nIB.Lat = Lat
                nIB.Lng = Lng
                nIB.Pushpin = ID
                nIB.ShowOnClickOnly = ShowOnClickOnly
                nIB.ImageURL = ImageURL
                nIB.LinkText = LinkText
                nIB.LinkURL = LinkURL
                InfoBoxes.Add(nIB)
                Return nIB
            End Function

            Public Function AddInfoBox(sHTMLContent As String, bVisible As Boolean, bShowOnClickOnly As Boolean) As InfoBox
                Dim nIB As New InfoBox
                nIB.ID = ID & "_Infobox"
                nIB.HTMLContent = sHTMLContent
                nIB.Visible = bVisible
                nIB.ShowOnClickOnly = bShowOnClickOnly
                nIB.Pushpin = ID
                nIB.ImageURL = ImageURL
                nIB.LinkText = LinkText
                nIB.LinkURL = LinkURL
                InfoBoxes.Add(nIB)
                Return nIB
            End Function

            Public Function AddOnHoverInforBox(sTitle As String, sDescription As String) As InfoBox
                ' add a hoverring inforbox that only shows on mouseover
                Dim nIB As InfoBox = AddInfoBox(sTitle, sDescription, False, False)
                nIB.ShowTitle = True
                nIB.ShowCloseButton = False
                nIB.ImageURL = ImageURL
                nIB.LinkText = LinkText
                nIB.LinkURL = LinkURL
                nIB.Width = 300
                Return nIB
            End Function

            Public Function AddOnClickInforBox(sTitle As String, sDescription As String) As InfoBox
                ' add inforbox that shows when user clicks a pushpun
                Return AddInfoBox(sTitle, sDescription, False, True)
            End Function

            Public Sub AddImageToInfoBox(imgURL As String)
                ImageURL = imgURL
            End Sub

            Public Sub AddLinkToInfoBox(sLinkText As String, sLinkURL As String)
                LinkText = sLinkText
                sLinkURL = sLinkURL
            End Sub
        End Class

        Public Class Point
            ' the point class, holds a lat and lng attribute
            Property Lat As Double
            Property Lng As Double

            Public Sub New()
                'create a new classs
                MyBase.New()
            End Sub

            Public Sub New(Lat As Double, Lng As Double)
                ' new point
                Lat = Lat
                Lng = Lng
            End Sub
        End Class

        Public Sub SetPoint(Lat As Double, Lng As Double)
            ' update the longitude and latitude for the map
            Me.Lat = Lat
            Me.Lng = Lng
            bSetPosition = True
        End Sub

        Public Sub SetPoint(pT As Point)
            ' set the longitude and latitude for the map
            Lat = pT.Lat
            Lng = pT.Lng
            bSetPosition = True
        End Sub

        Public Sub CenterAndZoom(Lat As Double, Lng As Double, ZoomLevel As Integer)
            ' center and zoom a map to a particular point
            Lat = Lat
            Lng = Lng
            Zoom = ZoomLevel
            bSetPosition = True
        End Sub

        <Bindable(True), Category("Appearance"), DefaultValue(""), Localizable(True)> Property Text() As String
            Get
                Dim s As String = CStr(ViewState("Text"))
                If s Is Nothing Then
                    Return String.Empty
                Else
                    Return s
                End If
            End Get

            Set(ByVal Value As String)
                ViewState("Text") = Value
            End Set
        End Property

        Protected Overrides Sub OnLoad(e As EventArgs)
            ' on load of the map
            MyBase.OnLoad(e)
        End Sub

        Protected Overrides Sub OnPreRender(e As EventArgs)
            ' before the map is rendered
            MyBase.OnPreRender(e)
            
            Dim IncludeCss As HtmlControls.HtmlLink
            IncludeCss = New HtmlControls.HtmlLink

            IncludeCss.Attributes.Add("type", "text/css")
            IncludeCss.Attributes.Add("rel", "stylesheet")
            IncludeCss.Attributes.Add("media", "all")

            Page.ClientScript.RegisterClientScriptResource(GetType(BingMaps.BingMap), "jquery-1.8.3.min.js")
            Page.ClientScript.RegisterClientScriptResource(GetType(BingMaps.BingMap), "PrintElement.js")

            If UseGoogle = True Then
                Page.ClientScript.RegisterClientScriptResource(GetType(BingMaps.BingMap), "gmap3.min.js")
                Page.ClientScript.RegisterClientScriptInclude(GetType(BingMaps.BingMap), "GoogleMapsApi", [String].Format(CultureInfo.InvariantCulture, GoogleMapControlHref, ApiVersion))
            Else
                IncludeCss.Attributes.Add("href", "BingMaps.css")
                Page.ClientScript.RegisterClientScriptResource(GetType(BingMaps.BingMap), "BingMaps.js")
                Page.ClientScript.RegisterClientScriptResource(GetType(BingMaps.BingMap), "BingFromKML.js")
                Page.ClientScript.RegisterClientScriptInclude(GetType(BingMaps.BingMap), "BingMapsApi", [String].Format(CultureInfo.InvariantCulture, BingMapControlHref, ApiVersion))
            End If
            Page.Header.Controls.Add(IncludeCss)
            'Page.ClientScript.RegisterClientScriptInclude(GetType(BingMaps.BingMap), "JQuery", [String].Format(CultureInfo.InvariantCulture, JQuery))
        End Sub

        Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
            writer.Write(Text)
        End Sub

        Public Sub New()
            'create a new map
            MyBase.New()
            ' this supports setting up a html tag, here being input
            js = New StringBuilder
            Layers = New List(Of Layer)
            InfoBoxes = New List(Of InfoBox)
            Scripts = New List(Of Script)
            VisibleMapTypeIds = New List(Of String)
            MapOptions = New List(Of MapOption)
            InfoboxContents = New List(Of InfoboxContent)
            Result = ""

            ' script to store map location
            AddScript("createCookie", "name, value, days", _
                        "if (days)", _
                        "{", _
                        "var date = new Date();", _
                        "date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));", _
                        "var expires = ""; expires="" + date.toGMTString();", _
                        "}", _
                        "else var expires = """";", _
                        "document.cookie = name + ""="" + value + expires + ""; path=/""")
            ' read the cookie

            AddScript("readCookie", "name", _
                        "var nameEQ = name + ""="";", _
                        "var ca = document.cookie.split(';');", _
                        "for (var i = 0; i < ca.length; i++)",
                        "{", _
                        "var c = ca[i];", _
                        "while (c.charAt(0) == ' ') c = c.substring(1, c.length);", _
                        "if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);", _
                        "}", _
                        "return null;")
            ' create some scripts
            AddScript("printSelection", "node", _
                    "var content = node.innerHTML;", _
                    "var pwin = window.open('','print_content','width=100,height=100');", _
                    "pwin.document.open();", _
                    "pwin.document.write('<html><body onload=""window.print()"">'+content+'</body></html>');", _
                    "pwin.document.close();", _
                    "setTimeout(function(){pwin.close();},1000);")

            AddScript("printMap", "", _
                               "printSelection(document.getElementById('mapDiv'));return false;")


            AddScript("getLastPosition", "", _
                "var lastloc = readCookie('LastLocation');", _
                "return lastloc;")

            AddScript("SetZoom", "e", "<map>.setView({zoom:e});")
            AddScript("GetCenter", "", "return <map>.GetCenter;")
            AddScript("printMap1", "", "$('mapDiv').printElement();")

            
        End Sub

        Private Sub PushpinBuilder(ByVal ppParent As String, ByVal ppLocation As String, ppOffSet As String, ppLat As String, ppLng As String, ppOptions As String)
            js.AppendFormat(CultureInfo.InvariantCulture, "var {0}_ppOffset{1} = new Microsoft.Maps.Point({2});", ppParent, ppLocation, ppOffSet)
            If UseNewLine = True Then js.AppendLine()
            js.AppendFormat(CultureInfo.InvariantCulture, "var {0}_ppLoc{1} = new Microsoft.Maps.Location({2},{3});", _
                            ppParent, ppLocation, ppLat, ppLng)
            If UseNewLine = True Then js.AppendLine()
            js.AppendFormat(CultureInfo.InvariantCulture, "var {0}_pushpin{1} = new Microsoft.Maps.Pushpin({0}_ppLoc{1}, {{{2}}});", _
                        ppParent, ppLocation, ppOptions)
            If UseNewLine = True Then js.AppendLine()
        End Sub

        Private Sub DisplayLatLongBuilder(MapID As String)
            ' method display lat long
            js.AppendFormat(CultureInfo.InvariantCulture, "function {0}displayLatLong(e)", MapID)
            If UseNewLine = True Then js.AppendLine()
            js.Append("{")
            If UseNewLine = True Then js.AppendLine()
            js.AppendFormat(CultureInfo.InvariantCulture, "if (e.targetType == ""{0}"")", MapID)
            If UseNewLine = True Then js.AppendLine()
            js.Append("{")
            If UseNewLine = True Then js.AppendLine()
            js.AppendFormat(CultureInfo.InvariantCulture, "var {0}point = new Microsoft.Maps.Point(e.getX(), e.getY());", MapID)
            If UseNewLine = True Then js.AppendLine()
            js.AppendFormat(CultureInfo.InvariantCulture, "var {0}loc = e.target.tryPixelToLocation(point);", MapID)
            If UseNewLine = True Then js.AppendLine()
            'js.append("document.getElementById("textBox").value= loc.Lat + ", " + loc.Lng;
            js.Append("}")
            If UseNewLine = True Then js.AppendLine()
            js.Append("}")
            If UseNewLine = True Then js.AppendLine()
        End Sub


        Protected Overrides Sub Render(writer As HtmlTextWriter)
            ' read from saved session state
            'BingMapObject = System.Web.HttpContext.Current.Session("BingMapObject")
            'If IsNothing(BingMapObject) Then
            ' enable functionality to raise _doPostBack
            'Dim p As New PostBackOptions(Me)
            'writer.AddAttribute(HtmlTextWriterAttribute.Onclick, Page.ClientScript.GetPostBackEventReference(p))

            ' render the map
            ' the div container has not been specified
            ' create the div container
            writer.AddAttribute(HtmlTextWriterAttribute.Id, ContainerId)
            writer.AddStyleAttribute(HtmlTextWriterStyle.Width, Width.ToString())
            writer.AddStyleAttribute(HtmlTextWriterStyle.Height, Height.ToString())
            writer.AddStyleAttribute(HtmlTextWriterStyle.Position, "absolute")
            writer.RenderBeginTag(DefaultContainerTag)
            writer.RenderEndTag()

            If UseGoogle = True Then
                ' the google map is being used gmap3

                AddScript("SaveZoom", "", "document.getElementById('zoomLevel').innerText = <map>.getZoom();")
                AddScript("SaveCenter", "", "document.getElementById('centerLocation').innerText = <map>.getCenter();")

                js.AppendFormat(CultureInfo.InvariantCulture, "var mDiv = document.getElementById('{0}');", ContainerId)
                js.AppendFormat(CultureInfo.InvariantCulture, "var {0}_options = {{{1}}};", BingMapVariableName, Me.GetOptions)
                js.AppendFormat(CultureInfo.InvariantCulture, "var {0} = new google.maps.Map(mDiv, {0}_options);", BingMapVariableName)

                ' add scripts on the mapcontrol
                For Each mScript In Scripts
                    mScript.Render(js, BingMapVariableName)
                Next

                ' add layers etc
                For Each curLayer As Layer In Layers
                    'js.AppendFormat(CultureInfo.InvariantCulture, "var {0} = new Microsoft.Maps.EntityCollection();", curLayer.ID)
                    If UseNewLine = True Then js.AppendLine()
                    ' build pushpins on the layer
                    For Each pP As PushPin In curLayer.Pushpins
                        pP.Render(js, BingMapVariableName, BingMapVariableName)
                    Next
                    ' render the bounds of pushpins
                    If curLayer.SetToBounds = True Then
                        Dim nBounds As New Bounds
                        For Each pT In curLayer.Pushpins
                            nBounds.AddPoint(pT.Lat, pT.Lng)
                        Next
                        nBounds.Render(js, curLayer.ID, BingMapVariableName)
                    End If
                Next

                ' build infoboxes
                For Each pP As InfoBox In InfoBoxes
                    pP.Render(js, BingMapVariableName)
                Next
                ' set options
                For Each mOption In MapOptions
                    mOption.Render(js, BingMapVariableName)
                Next

                ' change infobox contents
                For Each mContent As InfoboxContent In InfoboxContents
                    mContent.render(js)
                Next

                Result = js.ToString
            Else
                ' add the map options
                AddScript("mapViewChanged", "", _
                            "var curLocation = <map>.getCenter();", _
                            "var curLat = curLocation.latitude;", _
                            "var curLon = curLocation.longitude;", _
                            "createCookie('LastLocation', curLat + ':' + curLon + ':' + map.getZoom(), 30);")
                ' remove the pin
                AddScript("removePin", "e", _
                                   "var indexOfPinToRemove = <map>.entities.indexOf(e.target);", _
                                   "<map>.entities.removeAt(indexOfPinToRemove);")

                AddScript("hideAllEntities", "", "<map>.entities.setOptions({visible:false});")
                AddScript("showAllEntities", "", "<map>.entities.setOptions({visible:true});")
                AddScript("SetCenter", "lat,lng", _
                                   "var <map>_Center = new Microsoft.Maps.Location(lat,lng);", _
                                   "<map>.setView({center: <map>_Center});")
                AddScript("GetMapEntity", "id", "return <map>.entities.get(id);")
                AddScript("addPin", "e", _
                                   "if (e.targetType == ""<map>"")",
                                   "{", _
                                    "var map_point = new Microsoft.Maps.Point(e.getX(), e.getY());", _
                                    "var map_loc = e.target.tryPixelToLocation(map_point);", _
                                    "var map_pin = new Microsoft.Maps.Pushpin(map_loc);", _
                                    "<map>.entities.push(map_pin);",
                                    "}")
                AddScript("mapClicked", "e", _
                                 "if (e.targetType == ""<map>"")",
                                           "{", _
                                            "var map_point = new Microsoft.Maps.Point(e.getX(), e.getY());", _
                                            "var map_loc = e.target.tryPixelToLocation(map_point);", _
                                            "}")

                ' load the module
                If UseBingTheme = True Then
                    js.Append("Microsoft.Maps.loadModule('Microsoft.Maps.Themes.BingTheme', { callback: function(){")
                    If UseNewLine = True Then js.AppendLine()
                End If

                If ShowTraffic = True Then
                    js.AppendFormat(CultureInfo.InvariantCulture, "Microsoft.Maps.loadModule('Microsoft.Maps.Traffic', {{ callback: function() {{ trafficLayer = new Microsoft.Maps.Traffic.TrafficLayer({0});", BingMapVariableName)
                    If UseNewLine = True Then js.AppendLine()
                    js.Append(" trafficLayer.show();}});")
                    If UseNewLine = True Then js.AppendLine()
                End If

                js.AppendFormat(CultureInfo.InvariantCulture, "var {0}_mapOptions = {{{1}}};", BingMapVariableName, GetOptions)
                If UseNewLine = True Then js.AppendLine()
                ' add map points, polygons etc here

                ' create the map control
                js.AppendFormat(CultureInfo.InvariantCulture, "var {0} = new Microsoft.Maps.Map(document.getElementById(""{1}""), {0}_mapOptions);", _
                                BingMapVariableName, ContainerId)
                If UseNewLine = True Then js.AppendLine()
                ' try and disable context menu
                'js.AppendFormat(CultureInfo.InvariantCulture, "$('{0}').observe(""contextmenu"", function(e){{e.stop();}});", BingMapVariableName)
                'If UseNewLine = True Then js.AppendLine()


                ' add the layers
                For Each curLayer As Layer In Layers
                    js.AppendFormat(CultureInfo.InvariantCulture, "var {0} = new Microsoft.Maps.EntityCollection();", curLayer.ID)
                    If UseNewLine = True Then js.AppendLine()
                    ' build pushpins on the layer
                    For Each pP As PushPin In curLayer.Pushpins
                        pP.Render(js, curLayer.ID, BingMapVariableName)
                    Next

                    ' render the bounds of pushpins
                    If curLayer.SetToBounds = True Then
                        Dim nBounds As New Bounds
                        For Each pT In curLayer.Pushpins
                            nBounds.AddPoint(pT.Lat, pT.Lng)
                        Next
                        nBounds.Render(js, curLayer.ID, BingMapVariableName)
                    End If

                    ' build the polygons in the layer
                    For Each pp As Polygon In curLayer.Polygons
                        pp.Render(js, curLayer.ID, BingMapVariableName)
                    Next

                    ' build the polylines in the layer
                    For Each pp As Polyline In curLayer.Polylines
                        pp.Render(js, curLayer.ID, BingMapVariableName)
                    Next

                    ' add layer to the map
                    js.AppendFormat(CultureInfo.InvariantCulture, "{0}.entities.push({1});", BingMapVariableName, curLayer.ID)
                    If UseNewLine = True Then js.AppendLine()

                    ' code to hide and show layer
                    js.AppendFormat(CultureInfo.InvariantCulture, "function {0}showLayer()", curLayer.ID)
                    If UseNewLine = True Then js.AppendLine()
                    js.Append("{")
                    If UseNewLine = True Then js.AppendLine()
                    js.AppendFormat(CultureInfo.InvariantCulture, "{0}.setOptions({{visible:true}});", curLayer.ID)
                    If UseNewLine = True Then js.AppendLine()
                    js.Append("}")
                    If UseNewLine = True Then js.AppendLine()

                    js.AppendFormat(CultureInfo.InvariantCulture, "function {0}hideLayer()", curLayer.ID)
                    If UseNewLine = True Then js.AppendLine()
                    js.Append("{")
                    If UseNewLine = True Then js.AppendLine()
                    js.AppendFormat(CultureInfo.InvariantCulture, "{0}.setOptions({{visible:false}});", curLayer.ID)
                    If UseNewLine = True Then js.AppendLine()
                    js.Append("}")
                    If UseNewLine = True Then js.AppendLine()
                Next

                ' add a layer for all infoboxes
                js.AppendFormat(CultureInfo.InvariantCulture, "var {0}_InforBoxes = new Microsoft.Maps.EntityCollection();", BingMapVariableName)
                If UseNewLine = True Then js.AppendLine()
                ' build infoboxes
                For Each pP As InfoBox In InfoBoxes
                    pP.Render(js, BingMapVariableName)
                Next
                ' add the inforbox layer to the map
                js.AppendFormat(CultureInfo.InvariantCulture, "{0}.entities.push({0}_InforBoxes);", BingMapVariableName)
                If UseNewLine = True Then js.AppendLine()

                ' change polygon color
                If Len(ChangeColor) > 0 Then
                    js.Append(ChangeColor)
                    If UseNewLine = True Then js.AppendLine()
                End If

                ' add map events
                ' map click event
                js.AppendFormat(CultureInfo.InvariantCulture, "{0}_Click = Microsoft.Maps.Events.addHandler({0}, 'click', mapClicked);", BingMapVariableName)
                If UseNewLine = True Then js.AppendLine()

                js.AppendFormat(CultureInfo.InvariantCulture, "{0}_ViewChanged = Microsoft.Maps.Events.addHandler({0}, 'viewchangeend', mapViewChanged);", BingMapVariableName)
                If UseNewLine = True Then js.AppendLine()


                'DisplayLatLongBuilder(BingMapVariableName)
                'js.AppendFormat(CultureInfo.InvariantCulture, "{0}_MouseMove = Microsoft.Maps.Events.addHandler({0}, 'mousemove', {0}displayLatLong);", BingMapVariableName)
                'If UseNewLine = True Then js.AppendLine()

                ' add map click event
                If AddPinAtClickPosition = True Then
                    'js.AppendFormat(CultureInfo.InvariantCulture, "{0}_AddOnClickAttach = Microsoft.Maps.Events.addHandler({0}, 'click', addPin);", BingMapVariableName)
                    'If UseNewLine = True Then js.AppendLine()
                    'js.AppendFormat(CultureInfo.InvariantCulture, "{0}_AddOnClick = Microsoft.Maps.Events.addHandler({0}, 'click', addPin);", BingMapVariableName)
                    'If UseNewLine = True Then js.AppendLine()
                End If
                ' show user current position
                If ShowCurrentPosition = True Then
                    GetCurrentPosition(js, BingMapVariableName)
                End If

                ' add scripts on the mapcontrol
                For Each mScript In Scripts
                    mScript.Render(js, BingMapVariableName)
                Next

                If bSetPosition = True Then
                    ' the user has specified a position to go to
                    js.AppendFormat(CultureInfo.InvariantCulture, "var {0}_setPosition = new Microsoft.Maps.Location({1},{2});", BingMapVariableName, Lat, Lng)
                    js.AppendFormat(CultureInfo.InvariantCulture, "{0}.setView({{center: {0}_setPosition, zoom: {2}}});", BingMapVariableName, Zoom)
                    If UseNewLine = True Then js.AppendLine()
                End If

                ' set options
                For Each mOption In MapOptions
                    mOption.Render(js, BingMapVariableName)
                Next

                If UseBingTheme = True Then js.Append("}});")
                Result = js.ToString
                'System.Web.HttpContext.Current.Session("BingMapObject") = Result
                'Else
                'Result = BingMapObject.ToString
            End If
            Page.ClientScript.RegisterStartupScript(Me.[GetType](), ContainerId, Result, True)
        End Sub

        ' lets try and put this before render
        Public Sub PolygonChangeColor(ByVal PolygonID As String, FillColor As System.Drawing.Color, StrokeColor As System.Drawing.Color)
            Dim jx As New StringBuilder
            jx.AppendFormat(CultureInfo.InvariantCulture, "var {0}_polygonToChange = map.entities.get({0});", PolygonID)
            If UseNewLine = True Then jx.AppendLine()
            jx.AppendFormat(CultureInfo.InvariantCulture, "{0}_polygonToChange.setOptions({{fillColor: {1}, strokeColor: {2}}});", PolygonID, GetARGB(FillColor), GetARGB(StrokeColor))
            If UseNewLine = True Then jx.AppendLine()
            ChangeColor = jx.ToString
        End Sub

        Public Function ImportDBF(ByVal sPathName As String, ByVal sFileName As String) As DataTable
            ' open a datatable from dbf file
            Try
                Dim oConn As New System.Data.Odbc.OdbcConnection()
                oConn.ConnectionString = "Driver={Microsoft dBase Driver (*.dbf)};SourceType=DBF;SourceDB=" & sPathName & ";Exclusive=No; NULL=NO;DELETED=NO;BACKGROUNDFETCH=NO;"
                oConn.Open()
                Dim oCmd As System.Data.Odbc.OdbcCommand = oConn.CreateCommand()
                oCmd.CommandText = "SELECT * FROM '" & sPathName & sFileName & "';"
                Dim dt As New DataTable()
                dt.Load(oCmd.ExecuteReader())
                oConn.Close()
                Return dt
            Catch ex As Exception
                Throw New Exception(ex.Message)
            End Try
        End Function


        Protected Overrides Sub CreateChildControls()
            'These controls allow the BingMap control to capture some values
            ' SpanLatLng, BoundsLatLng, and ZoomLevel values from a BingMap during a postback
            MyBase.CreateChildControls()
            Dim zoomLvl As New HtmlInputHidden()
            zoomLvl.ID = "zoomLevel"
            MyBase.Controls.Add(zoomLvl)

            'centerLocation
            Dim centerLocation As New HtmlInputHidden()
            centerLocation.ID = "centerLocation"
            MyBase.Controls.Add(centerLocation)

            'Dim clickLng As New HtmlInputHidden()
            '    clickLng.ID = curLngField
            '    MyBase.Controls.Add(clickLng)


            'Dim centerLatLng As New HtmlInputHidden()
            'centerLatLng.ID = id & _centerLatLngField
            'MyBase.Controls.Add(centerLatLng)

            'Dim spanLatLng As New HtmlInputHidden()
            'spanLatLng.ID = id & _spanLatLngField
            'MyBase.Controls.Add(spanLatLng)
            'Dim boundsLatLng As New HtmlInputHidden()
            'boundsLatLng.ID = id & _boundsLatLngField
            'MyBase.Controls.Add(boundsLatLng)

            'Dim zoomLevel As New HtmlInputHidden()
            'zoomLevel.ID = id & _Zoom
            'MyBase.Controls.Add(zoomLevel)
        End Sub

        'Public Sub RaisePostBackEvent(eventArgument As String)
        ''What it all boils down to is running server-side code 
        ''as a result of client side events. With some custom JavaScript, 
        ''we send the BingMap control the event we would like to be raised 
        ''along with any event arguments. 
        ''RaisePostBackEvent() parses the event request and raises 
        ''the proper server-side event which then runs the developer's code.
        '' If this isn't a call back we won't bother
        '    If Not CallBackHelper.IsCallBack Then
        '        Return
        '    End If
        '
        '' Always wrap callback code in a try/catch block
        '    Try
        'Dim ea As String() = eventArgument.Split("|"c)
        'Dim args As String() = Nothing
        'Dim pea As GPointEventArgs = Nothing
        'Dim evt As String = ea(0)
        '        Select Case evt
        '' GMap Click event sends the coordinates 
        '' of the click as event argument
        '            Case "GMap_Click"
        '                args = ea(1).Split(","c)
        '                pea = New GPointEventArgs(Single.Parse(args(0)), Single.Parse(args(1)), Me)
        '                Me.OnClick(pea)
        '                Exit Select
        '' GMarker Click event sends the coordinates of 
        '' the click as event argument
        '            Case "GMarker_Click"
        '                args = ea(1).Split(","c)
        'Dim gp As New GPoint(Single.Parse(args(0)), Single.Parse(args(1)))
        'Dim gm As New GMarker(gp, args(2))
        '                pea = New GPointEventArgs(gp, gm)
        '                Me.OnMarkerClick(pea)
        '                Exit Select
        '' GMap Move Start event sends the 
        '' coordinates of the center of the 
        '' map where the move started
        '            Case "GMap_MoveStart"
        '                args = ea(1).Split(","c)
        '                pea = New GPointEventArgs(Single.Parse(args(0)), Single.Parse(args(1)))
        '                Me.OnMoveStart(pea)
        '                Exit Select
        '' GMap Move End event sends the 
        '' coordinates of the center of the 
        '' map where the move ended
        '            Case "GMap_MoveEnd"
        '                args = ea(1).Split(","c)
        '                pea = New GPointEventArgs(Single.Parse(args(0)), Single.Parse(args(1)))
        '                Me.OnMoveEnd(pea)
        '                Exit Select
        ' GMap Zoom event sends the old and new zoom levels
        '            Case "GMap_Zoom"
        '                args = ea(1).Split(","c)
        'Dim zea As New GMapZoomEventArgs(Integer.Parse(args(0)), Integer.Parse(args(1)))
        '                Me.OnZoom(zea)
        '                Exit Select
        '            Case Else
        '' Default: we don't know what the 
        '' client was trying to do
        '                CallBackHelper.Write([String].Empty)
        '                Exit Select
        '        End Select
        '' Had some odd Thread Abort Exceptions 
        '' going around. Something to do with 
        '' the default behavior of Response.End. 
        '' Just ignore these exceptions
        '    Catch e As Exception
        '        If Not (TypeOf e Is System.Threading.ThreadAbortException) Then
        '            CallBackHelper.HandleError(e)
        '        End If
        '    End Try
        'End Sub

        Private Sub BingMap_Load(sender As Object, e As EventArgs) Handles Me.Load
            
        End Sub

        Private Sub BingMap_PreRender(sender As Object, e As EventArgs) Handles Me.PreRender
            
        End Sub
    End Class
End Namespace